/**
 * 作文批注插件
 * 依赖于 jQuery、layer、Rangy
 *
 * @Author mtr
 * @since 2021-09-18
 *
 */
;(function ($, layer, rangy, window, document, undefined) {
    'use strict';

    const util = {
        getByClassName: function (target, className) {
            if ($(target).hasClass(className)) {
                return target;
            }
            var parents = $(target).parents('.' + className);
            if (parents.length > 0) {
                return parents.get(0);
            }
            return null;
        },
        setInsertionStyle: function ($insertion, $insertionValue, value) {
            var lineWidthMax = 300, line = 1, lineWidth = lineWidthMax, totalWidth = 0;
            var lowercaseRegx = /^[a-z]+$/, uppercaseRegx = /^[A-Z]+$/, numberRegx = /^[0-9]+$/, hanziRegx = /^[\u4E00-\u9FA5]+$/;
            var lowercaseWidth = 9, uppercaseWidth = 12, numberWidth = 10, hanziWidth = 16;
            for (let i = 0; i < value.length; i++) {
                var char = value.charAt(i);
                if (lowercaseRegx.test(char)) {
                    totalWidth += lowercaseWidth;
                } else if (uppercaseRegx.test(char)) {
                    totalWidth += uppercaseWidth;
                } else if (numberRegx.test(char)) {
                    totalWidth += numberWidth;
                } else if (hanziRegx.test(char)) {
                    totalWidth += hanziWidth;
                } else {
                    totalWidth += 5;
                }
            }

            if ((line = Math.ceil(totalWidth / lineWidthMax)) <= 1) {
                lineWidth = totalWidth > 35 ? totalWidth : 35;
            }
            $insertion.css("margin-bottom", line * 20 + 10 + 'px');
            $insertionValue.css("width", lineWidth + 'px');
            $insertionValue.text(value);
        }
    };

    // 常量
    const BASE_CLASS = "td-annotate-container"; // 根标签类名
    const HIGHLIGHTER_CLASS = "td-annotate-highlighter"; // 高亮样式
    const MARK_CLASS = "td-annotate-mark"; // 功能统一样式
    const NOTE_CLASS = "td-annotate-note"; // 注释样式
    const STRIKETHROUGH_CLASS = "td-annotate-strikethrough"; // 删除线样式
    const INSERTION_CLASS = "td-annotate-insertion"; // 插入文本样式
    const NOTE_TYPE = "note"; // 注释类型
    const STRIKETHROUGH_TYPE = "strikethrough"; // 删除线类型
    const INSERTION_TYPE = "insertion"; // 插入文本类型

    var autoIncrement = 1;

    var TdAnnotate = function (elem, config) {
        this.config = config;
        // console.log("startConfig", config);
        this.$elem = $(elem);
        this.$elem_source = this.$elem.clone();

        this.$elem.addClass(BASE_CLASS);
        this.init();
    };

    // 默认配置
    TdAnnotate.DEFAULT_CONFIG = {

    };

    // 插件初始化
    TdAnnotate.prototype.init = function () {
        if (!this.$elem.children().hasClass('td-annotate-text-area')) {
            var textArea = $('<div class="td-annotate-text-area"></div>').html(this.$elem.html());
            this.$elem.html(textArea);
        }
        if (!this.$elem.attr("id")) {
            this.$elem.attr("id", BASE_CLASS + autoIncrement++);
        }

        this.highlighter = rangy.createHighlighter();
        this.highlighter.addClassApplier(rangy.createClassApplier(HIGHLIGHTER_CLASS, {}));
        // 添加标记高亮事件
        this.$elem.on("mouseup", () => {this.mouseup()});
        // 添加点击事件
        this.$elem.on("click", (e) => {this.click(e)});
    };

    /**
     * 选中文字添加高亮事件
     */
    TdAnnotate.prototype.mouseup = function () {
        var isSelect = false, hasSelect = false;
        var selection = rangy.getSelection();
        var ranges = selection.getAllRanges();
        $.each(ranges, function (si, s) {
            $.each(s.getNodes(), function (i, n) {
                hasSelect = true;
                if (3 == n.nodeType){
                    n = n.parentNode;
                }
                if ($(n).hasClass(HIGHLIGHTER_CLASS) || $(n).hasClass(MARK_CLASS) || $(n).parents('.' + MARK_CLASS).length > 0){
                    isSelect = true;
                }
            });
        });
        if (isSelect) {
            this.highlighter.unhighlightSelection();
        } else if (hasSelect) {
            this.highlighter.highlightSelection(HIGHLIGHTER_CLASS);
            this.openOperationMenu();
        }
        selection.removeAllRanges();
    };

    /**
     * 点击事件
     */
    TdAnnotate.prototype.click = function (e) {
        var markDom = util.getByClassName(e.target, MARK_CLASS);
        if (markDom) {
            this.openEditMarkMenu(markDom);
            return;
        }
        var highlighterDom = util.getByClassName(e.target, HIGHLIGHTER_CLASS);
        if (highlighterDom) {
            this.openOperationMenu(highlighterDom);
            return;
        }
        if (this.firstOpen) {
            this.firstOpen = false;
        } else {
            layer.close(this.tipsIndex);
        }
    };

    /**
     * 打开编辑标签操作
     */
    TdAnnotate.prototype.openEditMarkMenu = function (selected) {
        var editMarkMenu = '', type = $(selected).data('type');
        if (type === NOTE_TYPE || type === INSERTION_TYPE) {
            editMarkMenu += this.createMenuElement('编辑', 'editMark') + ' | ';
        }
        editMarkMenu += this.createMenuElement('恢复', 'resetMark');
        layer.close(this.tipsIndex);
        // 打开弹窗
        this.tipsIndex = layer.tips([
            '<div>', editMarkMenu, '</div>'
        ].join(""), selected, {
            tips: [1, '#3595cc'],
            time: 10000
        });
        // this.firstOpen = true;
        this.currentOperationSelected = selected;
    };

    /**
     * 创建菜单元素
     */
    TdAnnotate.prototype.createMenuElement = function (name, method) {
        return '<span style="cursor:pointer;" onclick="$(\'#' + this.$elem.attr("id") + '\').tdAnnotate(\'' + method + '\')">' + name + '</span>';
    };

    /**
     * 获取选中的元素
     */
    TdAnnotate.prototype.getSelectedElement = function () {
        var selected = this.$elem.find("." + HIGHLIGHTER_CLASS);
        if (selected.length > 0) {
            return selected.get(0);
        }
        return null;
    };

    /**
     * 打开操作菜单
     */
    TdAnnotate.prototype.openOperationMenu = function (selected) {
        selected = selected || this.getSelectedElement();
        if (selected) {
            layer.close(this.tipsIndex);
            this.tipsIndex = layer.tips([
                '<div>',
                this.createMenuElement('注释', 'note'), " | ",
                this.createMenuElement('删除', 'strikethrough'), " | ",
                this.createMenuElement('插入', 'insertion'),
                '</div>'
            ].join(""), selected, {
                tips: [1, '#3595cc'],
                time: 10000
            });
            this.firstOpen = true;
            this.currentOperationSelected = selected;
        }
    };

    /**
     * 注释
     */
    TdAnnotate.prototype.note = function () {
        layer.close(this.tipsIndex);
        var selected = this.currentOperationSelected;
        if (selected) {
            layer.prompt({
                formType: 2,
                title: '请输入注释'
            }, function(value, index, elem){
                layer.close(index);
                $(selected).removeClass(HIGHLIGHTER_CLASS);
                $(selected).addClass(MARK_CLASS).addClass(NOTE_CLASS);
                $(selected).attr("data-node", value);
                $(selected).attr("onmouseover", "this.dataset.nodeTipsIndex = layer.tips(this.dataset.node || '--', this, {tips: 1, time: 60000})");
                $(selected).attr("onmouseout", "let nodeTipsIndex = this.dataset.nodeTipsIndex,i = setTimeout(()=>{layer.close(nodeTipsIndex)}, 2000)");
                $(selected).attr("data-type", NOTE_TYPE);
            });
            this.currentOperationSelected = null;
        }
    };

    /**
     * 删除线
     */
    TdAnnotate.prototype.strikethrough = function () {
        layer.close(this.tipsIndex);
        var selected = this.currentOperationSelected;
        if (selected) {
            $(selected).removeClass(HIGHLIGHTER_CLASS);
            $(selected).addClass(MARK_CLASS).addClass(STRIKETHROUGH_CLASS);
            $(selected).attr("data-type", STRIKETHROUGH_TYPE);
            this.currentOperationSelected = null;
        }
    };

    /**
     * 插入内容
     */
    TdAnnotate.prototype.insertion = function () {
        layer.close(this.tipsIndex);
        var selected = this.currentOperationSelected;
        if (selected) {
            layer.prompt({
                formType: 0,
                title: '请输入插入内容'
            }, function(value, index, elem){
                layer.close(index);
                var $insertion = $('<span></span>').addClass(MARK_CLASS).addClass(INSERTION_CLASS);
                $insertion.attr("data-type", INSERTION_TYPE);
                var $insertionTxt = $('<span class="td-annotate-insertion-txt"></span>');
                $insertionTxt.attr("onmouseover", "$('.td-annotate-insertion-txt').css('z-index',1),$(this).css('z-index',2)");
                var $insertionValue = $('<span class="td-annotate-insertion-value"></span>');
                // 设置插入框样式
                util.setInsertionStyle($insertion, $insertionValue, value);
                $(selected).after($insertion.append($insertionTxt.append($insertionValue)));
                $(selected).after($(selected).html());
                $(selected).remove();
            });
            this.currentOperationSelected = null;
        }
    };

    /**
     * 编辑操作
     */
    TdAnnotate.prototype.editMark = function () {
        layer.close(this.tipsIndex);
        var selected = this.currentOperationSelected;
        if (selected) {
            switch ($(selected).data("type")) {
                case NOTE_TYPE:
                    var node = $(selected).data('node');
                    layer.prompt({
                        value: node,
                        formType: 2,
                        title: '请输入注释'
                    }, function(value, index, elem){
                        layer.close(index);
                        $(selected).attr("data-node", value);
                    });
                    break;
                case INSERTION_TYPE:
                    var text = $(selected).find('.td-annotate-insertion-value').text();
                    layer.prompt({
                        value: text,
                        formType: 0,
                        title: '请输入插入内容'
                    }, function(value, index, elem){
                        layer.close(index);
                        // 设置插入框样式
                        util.setInsertionStyle($(selected), $(selected).find('.td-annotate-insertion-value'), value);
                    });
                    break;
            }
            this.currentOperationSelected = null;
        }
    };

    /**
     * 删除操作
     */
    TdAnnotate.prototype.resetMark = function () {
        layer.close(this.tipsIndex);
        var selected = this.currentOperationSelected;
        if (selected) {
            switch ($(selected).data("type")) {
                case NOTE_TYPE:
                case STRIKETHROUGH_TYPE:
                    $(selected).after($(selected).html()).remove();
                    break;
                case INSERTION_TYPE:
                    $(selected).remove();
                    break;
            }
            this.currentOperationSelected = null;
        }
    };


    const DATA_KEY = 'td-annotate';

    $.fn.tdAnnotate = function (option) {
        var value,
            args = Array.prototype.slice.call(arguments, 1);

        this.each(function () {
            var $this = $(this),
                data = $this.data(DATA_KEY),
                options = $.extend({}, TdAnnotate.DEFAULT_CONFIG, typeof option === 'object' && option);

            if (typeof option === 'string') {
                if (!data) {
                    return;
                }
                switch (option) {
                    case "note":
                        data.note();
                        break;
                    case "strikethrough":
                        data.strikethrough();
                        break;
                    case "insertion":
                        data.insertion();
                        break;
                    case "editMark":
                        data.editMark();
                        break;
                    case "resetMark":
                        data.resetMark();
                        break;
                    case "destroy":
                        $this.removeData(DATA_KEY);
                        $this.html(data.$elem_source.html());
                        break;
                }
            }

            if (!data) {
                $this.data(DATA_KEY, new TdAnnotate(this, options));
            }
        });

        return typeof value === 'undefined' ? this : value;
    };

    $(function () {
        $('.' + BASE_CLASS).tdAnnotate();
    });

})(jQuery, layer, rangy, window, document,);
